package com.in.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.in.dto.EmailDto;
import com.in.dto.LoginDto;
import com.in.entity.CustomerUserDetails;
import com.in.entity.InUser;
import com.in.mapper.InUserMapper;
import com.in.service.InUserService;
import com.in.utils.*;
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @ClassName LljUserServiceImpl
 * @Description
 * @Author liulianjia
 * @Date 2024/11/25 17:16
 */
@Slf4j
@Service
public class InUserServiceImpl implements InUserService {

    @Resource
    private AuthenticationManager manager;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private SendEmailUtil sendEmailUtil;
    @Resource
    private InUserMapper userMapper;

    @Override
    public List<InUser> listAll() {
        return List.of();
    }

    @Override
    public InUser getById(String userId) {
        return null;
    }

    @Override
    public int insert(InUser user) {
        return 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result update(InUser user, String token, MultipartFile file) {
        String userId = JwtUtil.getUserId(token);
        user.setUserId(userId);
        //如果有图片
        if (file != null) {
            //TODO 上传图片至云服务器
        }
        user.setUpdateBy(userId);
        user.setUpdateTime(LocalDateTime.now());
        if (userMapper.update(user) > 0) {
            //重新刷新缓存
            InUser inUser = userMapper.getById(userId);
            CustomerUserDetails userDetails = (CustomerUserDetails) redisUtil.get(token);
            BeanUtils.copyProperties(user, userDetails);
            long expireTime = redisUtil.getExpire(token);
            redisUtil.set(token, userDetails, expireTime);
            JSONObject json = new JSONObject(16);
            json.put("token", token);
            json.put("expire", expireTime);
            json.put("userInfo", userDetails);
            return Result.success(json);
        }
        return Result.err(StatusCode.ERROR, "修改失败");
    }

    @Override
    public int delete(InUser user) {
        return 0;
    }

    @Override
    public Result login(LoginDto dto, HttpServletRequest request) throws IOException {
        String userName = dto.getUsername();
        JSONObject json;
        if ("password".equals(dto.getType())) {
            //密码登录
            try {
                Authentication userAuthentication = new UsernamePasswordAuthenticationToken(userName, dto.getPassword());
                Authentication authenticate = manager.authenticate(userAuthentication);
                if (!authenticate.isAuthenticated()) {
                    return Result.err(StatusCode.ERROR, "登录失败！");
                }
                CustomerUserDetails user = (CustomerUserDetails) authenticate.getPrincipal();
                //登录
                json = userInfo(user, request);
            } catch (Exception e) {
                return Result.err(StatusCode.ERROR, e.getMessage());
            }
        } else {
            //验证码登录
            if (redisUtil.hasKey(CommonConstant.PREFIX_EMAIL + userName)) {
                //验证通过，查询用户信息
                QueryWrapper<InUser> wrapper = new QueryWrapper<>();
                wrapper.eq("username", userName).eq("status", 1);
                InUser user = userMapper.get(wrapper);
                if (ObjectUtils.isEmpty(user)) {
                    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                    //新用户注册
                    user = new InUser();
                    user.setUserId(SnowflakeIdWorker.gen());
                    user.setUsername(userName);
                    //生成随机密码
                    user.setPassword(bCryptPasswordEncoder.encode(RandomValueUtil.generateStr(8)));
                    user.setNickname(userName);
                    user.setCreateTime(LocalDateTime.now());
                    userMapper.insert(user);
                }
                CustomerUserDetails userDetails = new CustomerUserDetails();
                if (ObjectUtils.isNotEmpty(user)) {
                    BeanUtils.copyProperties(user, userDetails);
                    json = userInfo(userDetails, request);
                } else {
                    return Result.err(StatusCode.ERROR, "登录失败，遇到了不可预知的错误，请重试！");
                }
            } else {
                return Result.err(StatusCode.ERROR, "验证码已过期，请重新获取！");
            }
        }
        return Result.success(json);
    }

    @Override
    public boolean getYzm(LoginDto dto) throws MessagingException, UnsupportedEncodingException {
        //生成随机验证码
        String code = RandomValueUtil.generateNum(6);
        String msg = "【LIULIAN JIA的个人网站】验证码：" + code + "，用于邮箱身份验证，3分钟内有效，请勿泄露和转发。\n如非本人操作，请忽略此邮件。";
        EmailDto emailDto = new EmailDto(dto.getUsername(), msg, code);
        return sendEmailUtil.sendAuthCode(emailDto);
    }

    @Override
    public boolean loginOut(String token) {
        //清除token
        redisUtil.del(token);
        redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + JwtUtil.getAccount(token) + token);
        log.info("用户退出登录，账号：{}", JwtUtil.getAccount(token));
        return true;
    }

    @Override
    public boolean restPassword(InUser user, String token) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        user.setUpdateTime(LocalDateTime.now());
        user.setUpdateBy(user.getUserId());
        if (userMapper.restPassword(user)) {
            //清除所有缓存
            redisUtil.delAll(CommonConstant.PREFIX_USER_TOKEN + user.getUsername() + ":*");
            redisUtil.del(token);
            return true;
        }
        return false;
    }

    /**
     * @Author liulianjia
     * @Description 查询用户信息
     * @Date 2024/11/26 16:47
     **/
    private JSONObject userInfo(CustomerUserDetails userDetails, HttpServletRequest request) throws IOException {
        JSONObject json = new JSONObject(3);
        String account = userDetails.getUsername();
        String password = userDetails.getPassword();

        //1.生成token
        String token = JwtUtil.sign(userDetails.getUserId(), account, userDetails.getUsername(), password);
        //2.设置token缓存有效时间 7天内有效
        long expireTime = JwtUtil.EXPIRE_TIME * 7;
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + account + token, token, expireTime / 1000);
        redisUtil.set(token, userDetails, JwtUtil.EXPIRE_TIME * 7 / 1000);

        json.put("token", token);
        json.put("expire", expireTime);

        //3.ip归属地
        String ip = request.getHeader("X-Real-IP") != null ? request.getHeader("X-Real-IP") : request.getRemoteAddr();
        userDetails.setIpAddr(IpUtil.getAddress(ip));

        //4.设置登录用户信息
        userDetails.setPassword("");
        json.put("userInfo", userDetails);

        return json;
    }
}
